﻿using HtmlAgilityPack;
using Microsoft.VisualBasic.Devices;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HtmlDocument = HtmlAgilityPack.HtmlDocument;

namespace WebDownload
{
    public partial class Form1 : Form
    {
        private  bool canStop = false;
        private Encoding encoding= Encoding.GetEncoding("gbk");
        public Form1()
        {
            InitializeComponent();
            System.Net.ServicePointManager.SecurityProtocol  = SecurityProtocolType.Tls12;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            bool isList = radioList.Checked;
            if(isList)
            {
                List<HtmlNode> nodeList = getNodeList(txtUrl.Text, txtPath.Text);
                if (nodeList == null)
                {
                    MessageBox.Show("解析章节信息失败！");
                    return;
                } 
                dataGridView1.Rows.Clear();
                for (int i=0;i<nodeList.Count;i++)
                {
                    string href=nodeList[i].Attributes["href"].Value.ToString();
                    Uri baseUri = new Uri(txtUrl.Text); 
                    Uri absoluteUri = new Uri(baseUri, href);
                    dataGridView1.Rows.Add(nodeList[i].InnerText,absoluteUri.ToString());
                }
                
            }
            else
            {
                HtmlNode node = getNode(txtUrl.Text, txtPath.Text);
                txtResult.Text = node.XPath + node.InnerText ;
            }

            
        }
        public HtmlNode getNode(string Url,string Xpath)
        {
            HtmlWeb htmlWeb = new HtmlWeb();
            htmlWeb.OverrideEncoding = encoding;
            HtmlDocument doc = htmlWeb.Load(Url);
            if (doc.Encoding != htmlWeb.OverrideEncoding)
            {
                htmlWeb.OverrideEncoding = Encoding.GetEncoding(doc.Encoding.BodyName);
                doc = htmlWeb.Load(Url);
                encoding = htmlWeb.OverrideEncoding;
            }
            HtmlNode htmlNode = doc.DocumentNode.SelectSingleNode(Xpath);
            return htmlNode;
        }
        public List<HtmlNode> getNodeList(string Url, string Xpath)
        {
            HtmlWeb htmlWeb = new HtmlWeb();
            htmlWeb.OverrideEncoding = encoding;
            HtmlDocument doc = htmlWeb.Load(Url);
            if (doc.Encoding != htmlWeb.OverrideEncoding)
            {
                htmlWeb.OverrideEncoding =  Encoding.GetEncoding(doc.Encoding.BodyName);
                doc = htmlWeb.Load(Url);
                encoding = htmlWeb.OverrideEncoding;
            }
            try
            {
                List<HtmlNode> htmlNode = doc.DocumentNode.SelectNodes(Xpath).ToList();
                return htmlNode;
            }
            catch(Exception e)
            {
            }
            return null;
            
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            canStop = false;
            SaveFileDialog saveFileDialog = new SaveFileDialog() { Title = "导出文本文件", Filter = "txt文件(*.txt)|*.txt" };
            if (saveFileDialog.ShowDialog() == DialogResult.Cancel)
                return;
            DataTable dt = new DataTable();
            for(int i=0;i<dataGridView1.ColumnCount;i++)
            {
                dt.Columns.Add(dataGridView1.Columns[i].Name);
            }
            for (int j = 0; j < dataGridView1.RowCount; j++)
            {
                dt.Rows.Add(dataGridView1.Rows[j].Cells[0].Value, dataGridView1.Rows[j].Cells[1].Value);
            }

            if (dt.Rows.Count>0)
            {
                //章节数量
                int allCount = dt.Rows.Count;
                this.progressSave.Maximum = allCount;
                this.progressSave.Value = 0;
                //大于200章分开下载再合并
                if (allCount/200>1)
                {
                    int fileCount = allCount / 200 + (allCount % 200 == 0 ? 0 : 1);
                    String[] file = new String[fileCount];
                    for (int f=0;f<fileCount;f++)
                    {
                        Thread thread = new Thread(() => {
                            int page = f;
                            
                            for (int i = 0; i < 200; i++)
                            {
                                if (i + 200 * page >= allCount || page >= fileCount)
                                {
                                    break;
                                }
                                else
                                {
                                    if (!canStop)
                                    {
                                        if(!downLoad(dt.Rows[i + 200 * page]["Href"].ToString(), txtContent.Text, dt.Rows[i + 200 * page]["InnerText"].ToString(), saveFileDialog.FileName.Replace(".txt", "-" + (page + 1) + ".txt")))
                                        {
                                            txtResult.Invoke(new Action(() =>
                                            {
                                                this.txtResult.Text = $"{dt.Rows[i + 200 * page]["InnerText"].ToString()}--获取内容信息失败，任务已停止！" + this.txtResult.Text;
                                            }));
                                            return;
                                        }
                                    }
                                    else
                                    {
                                        txtResult.Invoke(new Action(() =>
                                        {
                                            this.txtResult.Text ="任务已停止！";
                                            this.progressSave.Value = 0;
                                        }));
                                    }
                                    
                                }
                            }
                            
                        } );
                        thread.Start();
                        Thread.Sleep(50);
                        file[f] = saveFileDialog.FileName.Replace(".txt", "-" + (f + 1) + ".txt");
                    } 
                    Thread th = new Thread(() => {
                        while (this.progressSave.Value != this.progressSave.Maximum)
                        {
                            if(canStop)
                            {
                                return;
                            }
                            Thread.Sleep(500);

                        }
                        if (fileCount > 1)
                        {
                            combineFile(file, saveFileDialog.FileName);
                            txtResult.Invoke(new Action(() =>
                            {
                                this.txtResult.Text = $"合并完成！\r\n" + this.txtResult.Text;
                            }));
                            Thread.Sleep(2000);
                            foreach (var str in file)
                            {
                                try
                                {
                                    //删除xls临时的文件
                                    System.IO.File.Delete(str);
                                }
                                catch { }
                            }
                        }
                        else
                        {
                            Computer MyComputer = new Computer();
                            MyComputer.FileSystem.RenameFile(saveFileDialog.FileName.Replace(".txt", "-1.txt"), saveFileDialog.FileName);
                        }
                        if (MessageBox.Show("保存成功，是否打开文件？", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
                            System.Diagnostics.Process.Start(saveFileDialog.FileName);
                    });
                    th.Start();

                }
                else
                {
                    for(int i=0;i<dt.Rows.Count;i++)
                    {
                        if (!downLoad(dt.Rows[i]["Href"].ToString(), txtContent.Text, dt.Rows[i]["InnerText"].ToString(), saveFileDialog.FileName))
                        {
                            txtResult.Invoke(new Action(() =>
                            {
                                this.txtResult.Text = $"{dt.Rows[i]["InnerText"].ToString()}--获取内容信息失败，任务已停止！" + this.txtResult.Text;
                            }));
                            return;                            
                        } 
                    }
                    if (MessageBox.Show("保存成功，是否打开文件？", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
                        System.Diagnostics.Process.Start(saveFileDialog.FileName);
                }
                
            }
            else
            {
                MessageBox.Show("没有章节信息无法导出！");
            }
            
        }
        public bool downLoad(string url,string xpath,string zhang,string path)
        {
            Thread.Sleep(50);
            HtmlNode node = getNode(url, xpath);
            if(node==null)
            {
                return false;
            }
            string title = zhang.Replace("", "").Replace("\f", "").Replace("\t", "").Replace("\b", "").Replace("\r", "").Replace("\n", ""); 
            string content = node.InnerHtml;
            content = content.Replace("", "").Replace("\f", "").Replace("\t", "").Replace("\b", "").Replace("\r", "").Replace("\n", "").Replace("&nbsp;", "").Replace("<p>", "").Replace(txtSplit.Text, "\r\n\r\n");
            string pattern = @"<script>.*</script>";
            content = Regex.Replace(content, pattern, "");
            System.IO.StreamWriter sw = new System.IO.StreamWriter(path, true, Encoding.GetEncoding("gb2312"));
            sw.Write("\r\n\r\n" + title + "\r\n\r\n");            
            sw.Write(content);
            sw.Flush();
            sw.Close();
            txtResult.Invoke(new Action(() => {
                this.txtResult.Text = title + "  已下载\r\n" + (this.txtResult.Text.Length>500?"": this.txtResult.Text);

                this.progressSave.Value= this.progressSave.Value+1<=this.progressSave.Maximum? this.progressSave.Value + 1: this.progressSave.Maximum;
            })
            );
            return true;
        }
        public void combineFile(String[] infileName, String outfileName)
        {
            int b;
            int n = infileName.Length;
            FileStream[] fileIn = new FileStream[n];
            using (FileStream fileOut = new FileStream(outfileName, FileMode.Create))
            {
                for (int i = 0; i < n; i++)
                {
                    try
                    {
                        fileIn[i] = new FileStream(infileName[i], FileMode.Open);
                        while ((b = fileIn[i].ReadByte()) != -1)
                            fileOut.WriteByte((byte)b);
                        fileIn[i].Close();
                    }
                    catch (System.Exception ex)
                    {
                        fileIn[i].Close();
                    }
                    txtResult.Invoke(new Action(() =>
                    {
                        this.txtResult.Text = $"正在合并第{i}个文件\r\n" + this.txtResult.Text;
                    }));

                }
                fileOut.Close();
            }            
        }

        private void btnDel_Click(object sender, EventArgs e)
        {
            dataGridView1.Rows.Clear();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            canStop = true;
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            Encoding encoding = Encoding.GetEncoding(comboBox1.SelectedItem.ToString());
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            comboBox1.SelectedIndex = 0;
        }

        private void pictureBox1_MouseHover(object sender, EventArgs e)
        {
            PictureBox pb = (PictureBox)sender;
            tips.Visible = true;
            switch(pb.Tag.ToString())
            {
                case "网址":
                    tips.Text = "请输入小说章节页的网址，例如：https://www.12zw.la/8/8021/ ，https://www.12zw.la/8/8169/";
                    tips.Location = new Point(pb.Location.X + 40, pb.Location.Y+26 );
                    break;
                case "章节":
                    tips.Text = "请输入小说章节列表的Xpath，例如：//*[@id=\"list\"]/dl/dd/a , dl/dd/a ；如何获取Xpath：浏览器按F12进入调试模式，按Ctrl+Shitf+C，进入节点选择，选中章节列表中的一个，在下面的HTML中右键，Copy,Copy Xpath";
                    tips.Location = new Point(pb.Location.X + 40, pb.Location.Y +26);
                    break;
                case "内容":
                    tips.Text = "请输入小说内容的Xpath，例如：//*[@id=\"list\"]/dl/dd/a , dl/dd/a ；如何获取Xpath：浏览器按F12进入调试模式，按Ctrl+Shitf+C，进入节点选择，选中章节列表中的一个，在下面的HTML中右键，Copy,Copy Xpath";
                    tips.Location = new Point(groupBox1.Width+pb.Location.X-288 , pb.Location.Y+26);
                    break; 
                case "行分割":
                    tips.Text = "请输入小说内容的行或者段落的分割符，例如：<br><br>，</p></p>";
                    tips.Location = new Point(groupBox1.Width + pb.Location.X-288 , pb.Location.Y +26);
                    break;

            }
        }

        private void pictureBox1_MouseLeave(object sender, EventArgs e)
        {
            tips.Visible = false;
        }


    }

}
